#include <cmath>
#include <iostream>
#include <opencv2/opencv.hpp>
#include <vector>

#include <windows.h>

#include "PeopleGather.h"
#include "common.h"
#include "cv_puttext.h"
#include "yolov7.h"

void End_People_Gather(videoWriter& writer, PeopleGather& people_gather) {
    End_Writer(writer);
    for (auto it = people_gather.alert_rects.begin(); it != people_gather.alert_rects.end(); it++)
        it->second.rect_is_store = false;
}

void Store_Before_AlertFrame(videoWriter& writer, TimestampQueue& img_deque, PeopleGather& people_gather, std::string output_string, int time_threshold) {
    long long now_time = Get_Current_Timestamp();
    for (auto queue_it = img_deque.img_timestamp_deque.begin(); queue_it != img_deque.img_timestamp_deque.end(); queue_it++) {
        int before_img_diff = (now_time - queue_it->timestamp) / 1000;
        if (before_img_diff > time_threshold)
            break;  // 存储与当前帧相差30s的视频
        cv::Mat before_img = queue_it->image.clone();
        cv::putTextZH(before_img, output_string.c_str(), cv::Point(50, 50), CV_RGB(255, 0, 0), 60);
        for (auto it = people_gather.alert_rects.begin(); it != people_gather.alert_rects.end(); it++) {
            if (!it->second.rect_is_store)
                continue;
            if (int(before_img_diff % 10) % 2)
                people_gather.Draw_Single_AlertRect(before_img, it->first, cv::Scalar(0, 0, 255), queue_it->timestamp);
        }
        writer.alert_video.write(before_img);
    }
    writer.is_first_frame = false;
}

void Store_After_AlertFrame(cv::Mat src, videoWriter& writer, PeopleGather& people_gather, std::string output_string) {
    cv::Mat alert_src = src.clone();
    long long now_time = Get_Current_Timestamp();
    cv::putTextZH(alert_src, output_string.c_str(), cv::Point(50, 50), CV_RGB(255, 0, 0), 60);
    // 画出所有需要存储的框
    int store_time_diff = (now_time - writer.start_timestamp) / 1000;
    for (auto it = people_gather.alert_rects.begin(); it != people_gather.alert_rects.end(); it++) {
        if (!it->second.rect_is_store)
            continue;
        if (int(store_time_diff % 10) % 2)
            people_gather.Draw_Single_AlertRect(alert_src, it->first, cv::Scalar(0, 0, 255));
        if (writer.alert_img.empty())
            writer.alert_img = alert_src.clone();
    }
    writer.alert_video.write(alert_src);
}

int main() {
    srand(time(nullptr));
    // 读取网络模型
    Net_config YOLOV7_nets = {0.5, 0.5, "models/best(5).onnx"};
    YOLOV7 net(YOLOV7_nets, "YOLOV7");
    // 读取视频
    cv::VideoCapture cap("videos/cam.mp4");
    cv::Mat src;
    cap >> src;
    if (src.empty()) {
        std::cout << "读入视频失败" << std::endl;
    } else {
        std::cout << "读入视频成功" << std::endl;
    }
    // 创建人员聚集类
    PeopleGather people_gather = PeopleGather(300, 1, 8);  // 一个点包含其他点最大的距离、一个点包含其他点最小个数、框内多少数判断为聚集
    // 创建写入视频类
    videoWriter writer = Init_Single_Writer("peopleGather");

    TimestampQueue img_deque{500};
    while (!src.empty()) {
        img_deque.Push_Back(src.clone());
        std::vector<BoxInfo> res = net.detect(src);  // yolo检测结果
        ////画yolo检测框
        // for (size_t i = 0; i < res.size(); ++i)
        //{
        //	std::string class_name = net.class_names[res[i].label];
        //	std::string label = class_name;
        //	cv::rectangle(src, cv::Point(int(res[i].x1), int(res[i].y1)), cv::Point(int(res[i].x2), int(res[i].y2)), cv::Scalar(0, 0, 255), 2);
        //	cv::putText(src, label, cv::Point(int(res[i].x1), int(res[i].y1) - 5), cv::FONT_HERSHEY_SIMPLEX, 0.75, cv::Scalar(0, 255, 0), 1);
        // }
        people_gather.Alert_Detect(res);  // 人员聚集检测
        // 报警逻辑判断
        long long now_time = Get_Current_Timestamp();  // 获取当前时间戳
        for (auto it = people_gather.alert_rects.begin(); it != people_gather.alert_rects.end(); it++) {
            int gather_time_diff = (now_time - it->second.last_gather_time) / 1000;
            int last_alert_time_diff = (now_time - it->second.last_alert_time) / 1000;
            if (gather_time_diff > 10 && last_alert_time_diff > 10 * 60) {
                std::cout << "发送报警" << std::endl;
                // 发生报警
                it->second.last_alert_time = Get_Current_Timestamp();  // 更新上次报警时间
                it->second.rect_is_store = true;                       // 该报警框进行存储
                writer = Init_Single_Writer("peopleGather");
                writer.start_timestamp = Get_Current_Timestamp();
                writer.alert_video.open(writer.outfile_video_path, cv::VideoWriter::fourcc('X', 'V', 'I', 'D'), 10, cv::Size(src.cols, src.rows));
                writer.is_begin_store = true;  // 开始记录视频
                writer.is_first_frame = true;
            }
            // 画报警框
        }
        cap >> src;
        // 存储视频
        std::string output_string = "人员聚集";
        if (writer.is_first_frame)
            Store_Before_AlertFrame(writer, img_deque, people_gather, "人员聚集", 30);  // writer, 队列，人员聚集框，显示在左上角的字，队列中存储多少秒前的图片
        if (writer.is_begin_store)
            Store_After_AlertFrame(src, writer, people_gather, "人员聚集");  // 图片，writer, 人员聚集框，显示在左上角的字，队列中存储多少秒前的图片
        else
            continue;
        // 存储视频时间够，结束存储
        std::cout << (now_time - writer.start_timestamp) / 1000 << std::endl;
        int After_AlertFrame = 5;
        if (((now_time - writer.start_timestamp) / 1000) > After_AlertFrame) {
            std::cout << "End" << std::endl;
            End_People_Gather(writer, people_gather);
            Send_Message(writer);
            writer = Init_Single_Writer("peopleGather");
        }
    }
    cap.release();
    return 0;
}
